package com.tticar.invmanager.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.tticar.invmanager.common.Enum.CodeType;
import com.tticar.invmanager.common.utils.DateUtils;
import com.tticar.invmanager.entity.CodeCounter;
import com.tticar.invmanager.mapper.CodeCounterMapper;
import com.tticar.invmanager.service.ICodeCounterService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * <p>
 * 单号计数器 服务实现类
 * </p>
 *
 * @author jiwenbin
 * @since 2018-08-01
 */
@Service
@Scope("singleton")
public class CodeCounterServiceImpl extends ServiceImpl<CodeCounterMapper, CodeCounter> implements ICodeCounterService {

    /**
     * 获取编码
     * code = 前缀 + 中间部分 + 序号
     * @param codeType 编码类型前缀
     * @param middle 编码中间部分
     * @param length 序号长度
     * @return
     */
    @Override
    public String getCode(CodeType codeType, String middle, int length) {
        int counter = insertOrUpdate(codeType.getCode(), 2, false);

        // 定长位数，不够前面补0
        String noStr = StringUtils.leftPad(String.valueOf(counter), length, "0");

        if (middle == null) {
            middle = "";
        }
        // 前缀 + 中间部分 + 序号
        return  codeType.getCode() + middle + noStr;

    }

    /**
     * 获取单号
     * code = 前缀 + 日期 + 序号
     * 防止并发造成code重复，这里必须是线程安全的
     * @param codeType
     * @return
     */
    @Override
    public synchronized String getOrderCode(CodeType codeType) {
        int length = 5;
        int counter = insertOrUpdate(codeType.getCode(), 0, true);

        String noStr = StringUtils.leftPad(String.valueOf(counter), length, "0"); // 定长位数，不够前面补0
        String dateStr = DateUtils.getInstance().dateToString(new Date(), DateUtils.yyyyMMdd1); // 今天日期(yyyyMMdd)

        // 前缀 + 日期 + 序号
        return  codeType.getCode() + dateStr + noStr;
    }

    @Override
    public synchronized String getOrderCodeByTenantId(CodeType codeType,Long tenantId) {
        int length = 5;
        int counter = insertOrUpdateByTenantId(codeType.getCode(), 0, true,tenantId);

        String noStr = StringUtils.leftPad(String.valueOf(counter), length, "0"); // 定长位数，不够前面补0
        String dateStr = DateUtils.getInstance().dateToString(new Date(), DateUtils.yyyyMMdd1); // 今天日期(yyyyMMdd)

        // 前缀 + 日期 + 序号
        return  codeType.getCode() + dateStr + noStr;
    }

    /**
     * 获取批号
     * code = 前缀 + 日期  + 小时+ 序号
     * 防止并发造成code重复，这里必须是线程安全的
     * @return
     */
    @Override
    public synchronized String getBatchCode() {
        int length = 2;
        int counter = insertOrUpdate("", 1, true);

        String noStr = StringUtils.leftPad(String.valueOf(counter), length, "0"); // 定长位数，不够前面补0
        String dateStr = DateUtils.getInstance().dateToString(new Date(), DateUtils.yyyyMMdd2); // 今天日期(yyyyMMddHH)

        // 前缀 + 日期 + 小时 + 序号
        return  "PC" + dateStr + noStr;
    }

    private int insertOrUpdate(String codeType, int type, boolean isByTime) {
        // 计数
        int counter = 1;
        // 1,获取当前租户的对应codeType 的计数器(只应该有一个)
        Wrapper wrapper = new EntityWrapper();
        wrapper.eq(CodeCounter.TYPE, type);
        wrapper.eq(StringUtils.isNoneBlank(codeType), CodeCounter.CODE_TYPE, codeType);
        CodeCounter codeCounter = this.selectOne(wrapper);

        // 2,新增或者更新计数器
        // 第一次获取计数器为空需要新增一条
        if (codeCounter == null) {
            CodeCounter codeCounterNew = new CodeCounter();
            codeCounterNew.setNumber(counter);
            codeCounterNew.setCodeType(StringUtils.isNoneBlank(codeType)? codeType : null);
            codeCounterNew.setType(type);
            this.insert(codeCounterNew);
        } else {
            // 计数器不为空则更新计数器
            // 如果需要根据今天时间初始化计数器 并且
            // 计数器更新时间是今天的，那么需要将计数器+1
            // 计数器更新时间不是今天的，那么需要将计数器初始化(number 默认=1)
            if (isByTime) {
                if (codeCounter.getUtime().after(DateUtils.getInstance().getCurrentDayStartTime())) {
                    counter = codeCounter.getNumber() + 1;
                }
            } else {
                counter = codeCounter.getNumber() + 1;
            }

            CodeCounter updater = new CodeCounter();
            updater.setId(codeCounter.getId());
            updater.setNumber(counter);
            updater.setUtime(new Date());
            this.updateById(updater);
        }
        return counter;
    }


    private int insertOrUpdateByTenantId(String codeType, int type, boolean isByTime,Long tenantId) {
        // 计数
        int counter = 1;
        // 1,获取当前租户的对应codeType 的计数器(只应该有一个)
        Wrapper wrapper = new EntityWrapper();
        wrapper.eq(CodeCounter.TYPE, type);
        wrapper.eq(StringUtils.isNoneBlank(codeType), CodeCounter.CODE_TYPE, codeType);
        wrapper.eq("tenant_id", tenantId);
        CodeCounter codeCounter = this.selectOne(wrapper);

        // 2,新增或者更新计数器
        // 第一次获取计数器为空需要新增一条
        if (codeCounter == null) {
            CodeCounter codeCounterNew = new CodeCounter();
            codeCounterNew.setNumber(counter);
            codeCounterNew.setCodeType(StringUtils.isNoneBlank(codeType)? codeType : null);
            codeCounterNew.setType(type);
            codeCounterNew.setTenantId(tenantId);
            this.insert(codeCounterNew);
        } else {
            // 计数器不为空则更新计数器
            // 如果需要根据今天时间初始化计数器 并且
            // 计数器更新时间是今天的，那么需要将计数器+1
            // 计数器更新时间不是今天的，那么需要将计数器初始化(number 默认=1)
            if (isByTime) {
                if (codeCounter.getUtime().after(DateUtils.getInstance().getCurrentDayStartTime())) {
                    counter = codeCounter.getNumber() + 1;
                }
            } else {
                counter = codeCounter.getNumber() + 1;
            }

            CodeCounter updater = new CodeCounter();
            updater.setId(codeCounter.getId());
            updater.setNumber(counter);
            updater.setUtime(new Date());
            updater.setTenantId(tenantId);
            this.updateById(updater);
        }
        return counter;
    }


}
